W poniższej pracy wyliczymy profile Partial Dependence Profiles (PDP) i Accumulated Local Dependence (ALE) dla wytrenowanych przez nas modeli. Otrzymane wyniki wykorzystamy do porównania modeli między sobą i szukania korelacji w naszym zbiorze danych. Dodatkowo porównamy otrzymane wyniki z wnioskami wynikającymi z poprzedniej pracy domowej, gdzie dla wybrancyh obserwacji wyliczaliśmy Ceteris Paribus.
import pandas as pd
import numpy as np
import dalex as dx
import pickle
np.random.seed = 42
X_train = pd.read_csv("./src/X_train.csv")
y_train = pd.read_csv("./src/y_train.csv")
X_test = pd.read_csv("./src/X_test.csv")
y_test = pd.read_csv("./src/y_test.csv")
X_train = X_train.drop("Unnamed: 0",axis=1)
y_train = y_train.drop("Unnamed: 0",axis=1)
# Gradient Boosting
gbc = pickle.load(open("./src/gbc.pickle", 'rb'))
# XGBoost
gbm = pickle.load(open("./src/gbm.pickle", 'rb'))
# Random Forest
rfc = pickle.load(open("./src/rfc.pickle", 'rb'))
# SVM
svm = pickle.load(open("./src/svm.pickle", 'rb'))
explainer_gbc = dx.Explainer(gbc, X_train, y_train,label="Gradient Boosting")
explainer_gbm = dx.Explainer(gbm, X_train, y_train,label="XGBoost")
explainer_svm = dx.Explainer(svm, X_train, y_train,label="SVM")
explainer_rfc = dx.Explainer(rfc, X_train, y_train,label="Random Forest")
Preparation of a new explainer is initiated -> data : 1199 rows 11 cols -> target variable : Parameter 'y' was a pandas.DataFrame. Converted to a numpy.ndarray. -> target variable : 1199 values -> model_class : sklearn.model_selection._search.GridSearchCV (default) -> label : Gradient Boosting -> predict function : <function yhat_proba_default at 0x00000211841A7B80> will be used (default) -> predict function : Accepts pandas.DataFrame and numpy.ndarray. -> predicted values : min = 0.00902, mean = 0.535, max = 0.993 -> model type : classification will be used (default) -> residual function : difference between y and yhat (default) -> residuals : min = -0.486, mean = 7.72e-06, max = 0.485 -> model_info : package sklearn A new explainer has been created! Preparation of a new explainer is initiated -> data : 1199 rows 11 cols -> target variable : Parameter 'y' was a pandas.DataFrame. Converted to a numpy.ndarray. -> target variable : 1199 values -> model_class : sklearn.model_selection._search.RandomizedSearchCV (default) -> label : XGBoost -> predict function : <function yhat_proba_default at 0x00000211841A7B80> will be used (default) -> predict function : Accepts pandas.DataFrame and numpy.ndarray. -> predicted values : min = 0.0178, mean = 0.534, max = 0.992 -> model type : classification will be used (default) -> residual function : difference between y and yhat (default) -> residuals : min = -0.498, mean = 0.000165, max = 0.418 -> model_info : package sklearn A new explainer has been created! Preparation of a new explainer is initiated -> data : 1199 rows 11 cols -> target variable : Parameter 'y' was a pandas.DataFrame. Converted to a numpy.ndarray. -> target variable : 1199 values -> model_class : sklearn.model_selection._search.GridSearchCV (default) -> label : SVM -> predict function : <function yhat_default at 0x00000211841A7AF0> will be used (default) -> predict function : Accepts pandas.DataFrame and numpy.ndarray. -> predicted values : min = 0.0, mean = 0.505, max = 1.0 -> model type : classification will be used (default) -> residual function : difference between y and yhat (default) -> residuals : min = -1.0, mean = 0.03, max = 1.0 -> model_info : package sklearn A new explainer has been created! Preparation of a new explainer is initiated -> data : 1199 rows 11 cols -> target variable : Parameter 'y' was a pandas.DataFrame. Converted to a numpy.ndarray. -> target variable : 1199 values -> model_class : sklearn.model_selection._search.RandomizedSearchCV (default) -> label : Random Forest -> predict function : <function yhat_proba_default at 0x00000211841A7B80> will be used (default) -> predict function : Accepts pandas.DataFrame and numpy.ndarray. -> predicted values : min = 0.0106, mean = 0.535, max = 0.999 -> model type : classification will be used (default) -> residual function : difference between y and yhat (default) -> residuals : min = -0.56, mean = -0.000809, max = 0.529 -> model_info : package sklearn A new explainer has been created!
pdp_gbm = explainer_gbm.model_profile()
Calculating ceteris paribus: 100%|█████████████| 11/11 [00:00<00:00, 25.88it/s]
pdp_gbm.plot()
Największa różnicą pomiędzy PDP, a Ceteris Paribus dla wybranych obserwacji jest to, że wykresy PDP są gładsze i mają mniejsze skoki w wartościach. Potwierdzają się także nasze wnioski z wyjaśnień Ceretis Paribus, gdzie zauważyliśmy, że zależność między oceną wina, a zawartością alkoholu jest bardziej złożona niż nam się wydawało. Tutaj widzimy, że największe prowdopodobieństwo zaklasyfikowania wina jako dobrego jest dla zawartości alkoholu z przedziału (11.6, 12.8). Ciekawe wykresy są także dla całkowitej zawartości dwutlenku siarki (total sulfur dioxide) i zawartości siarczanów (sulphates), gdzie w pierwszym mamy bardzo duży spadek, w drugim wzrost wartości predykcji naszego modelu.
pdp_gbc = explainer_gbc.model_profile()
pdp_rfc = explainer_rfc.model_profile()
pdp_svm = explainer_svm.model_profile()
Calculating ceteris paribus: 100%|█████████████| 11/11 [00:00<00:00, 23.97it/s] Calculating ceteris paribus: 100%|█████████████| 11/11 [00:47<00:00, 4.34s/it] Calculating ceteris paribus: 100%|█████████████| 11/11 [00:29<00:00, 2.65s/it]
pdp_gbm.plot([ pdp_rfc, pdp_svm,pdp_gbc])
Zdecydowanie modelem z najbardziej odstającymi wykresami dla wartości objaśnianych jest SVM. Wykresy dla zmiennych takich jak waga cząsteczek siarki (free sulfur dioxide), zawartości siarczynów (sulphates) i całkowita zawartość dwutlenku siarki (total sulfur dioxide) nie pokrywają się z resztą modeli. SVM wszędzie poza zminną density ma większe zmiany w wartościach. Dla reszty modeli wykresy idą prawie identyczne z największą różnicą w alkoholu gdzie Random Forest nie ma tak dużego skoku w zawartości alkoholu równej 11.5 .
ale_gbm = explainer_gbm.model_profile(type = 'accumulated')
Calculating ceteris paribus: 100%|█████████████| 11/11 [00:00<00:00, 23.86it/s] Calculating accumulated dependency: 100%|██████| 11/11 [00:01<00:00, 8.40it/s]
ale_gbm.result['_label_'] = "ALE XGBoost"
pdp_gbm.result['_label_'] = "PDP XGBoost"
ale_gbm.plot(pdp_gbm)
Dla wszystkich zmiennych wykresy PDP i ALE są równoległe co sugeruje, że wybrany przez nas model XGBoost dla danego zbioru danych jest addytywny. Najwięszka różnica w wartościach występuje przy zawartości siarczanów (sulphates).
ale_gbc = explainer_gbc.model_profile(type = 'accumulated')
ale_rfc = explainer_rfc.model_profile(type = 'accumulated')
ale_svm = explainer_svm.model_profile(type = 'accumulated')
Calculating ceteris paribus: 100%|█████████████| 11/11 [00:00<00:00, 26.19it/s] Calculating accumulated dependency: 100%|██████| 11/11 [00:01<00:00, 9.02it/s] Calculating ceteris paribus: 100%|█████████████| 11/11 [00:43<00:00, 3.98s/it] Calculating accumulated dependency: 100%|██████| 11/11 [00:01<00:00, 9.46it/s] Calculating ceteris paribus: 100%|█████████████| 11/11 [00:27<00:00, 2.54s/it] Calculating accumulated dependency: 100%|██████| 11/11 [00:01<00:00, 9.18it/s]
ale_rfc.result['_label_'] = "ALE Random Forest"
ale_gbc.result['_label_'] = "ALE Gradient Boosting"
ale_svm.result['_label_'] = "ALE SVM"
ale_gbm.plot([ale_rfc,ale_svm,ale_gbc])
Najbardziej od wykresów PDP, różnią się wykresy ALE dla modelu SVM. Zmniejszone zostało prawdopodobieństwo zaliczenia wina do dobrych przy zmianie wartości całkowitej zawartości dwutlenku siarki (total sulfur dioxide) oraz zwiększyło się się przy zmianie zawartości cząsteczek dwutlenku siarki (free sulfur dioxide). Dodatkowo przy wyliczaniu profili ALE SVM wydaje się mniej odstawać od reszty modeli niż w przypadku PDP. U wszystkich modeli pradopodobieństwo zaliczenia wina do dobrych w zależności od zawartości siarczanów (sulphates) podniosła się.
Z wytrenowanych przez nas modeli przy wyliczaniu profili PDP i ALE najbardziej odstawał SVM. Wykresy wartości predykcji były bardziej zmienne i miały większe różnice w wartościach. We wcześniejszej pracy domowej gdzie dla naszych modeli wyliczaliśmy permutacyjną ważność zmiennych SVM przypisywał free sulfur dioxide i total sulfur dioxide zdecydowanie wyższe wartości niż reszta modeli. Tutaj za to widzimy, że wartości prawdopoboieństwa w SVM są zdecydowanie bardziej wrażliwe na zmiany wartości tych zmiennych.